#region Using Statements
using System;
using System.Collections.Generic;


using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion



namespace Pauliver
{
    [Serializable]
    public class Collidable : Component
    {
        private bool _Moving;
        public Rectangle _BoundingBox;
        public Color[] _TextureData = null;
        public int _width;
        public int _height;
        public Matrix _Transform;

        public int Width
        {
            get { return _width; }
        }
        public int Height
        {
            get { return _height; }
        }
        public Matrix Transform
        {
            get { return _Transform; }
        }

        public Collidable()
        {
            base._Type = ComponentType.Collidable;
            _BoundingBox = new Rectangle();
            _Moving = false;
        }

        public bool Moving
        {
            get
            {
                return _Moving;
            }
            set
            {
                Settled_Ground sg = (Parent[ComponentType.Settled_Ground] as Settled_Ground);
                if (sg != null && value)
                {
                    sg.Settled = false;
                }
                _Moving = value;
            }
        }

        public Rectangle GetBoundingBox
        {
            get
            {
                return _BoundingBox;
            }
        }
        Game_Position gp;
        public override void Update(GameTime p_time)
        {
            gp = (base.Parent[ComponentType.Game_Position] as Game_Position);
            _BoundingBox.X = (int)gp.Position.X - (_width/2);
            _BoundingBox.Y = (int)gp.Position.Y - (_height/2);
        }

        public void CalcRectange(Texture2D p_Texture)
        {
#if PerPixelCollision
            _TextureData = new Color[p_Texture.Height * p_Texture.Width];
            p_Texture.GetData(_TextureData);
#endif
            _Transform = Matrix.Identity;
            _width = p_Texture.Width;
            _height = p_Texture.Height;

            _BoundingBox.X = (int) (base.Parent[ComponentType.Game_Position] as Game_Position).Position.X - (_width/2);
            _BoundingBox.Y = (int) (base.Parent[ComponentType.Game_Position] as Game_Position).Position.Y - (_height/2);
            _BoundingBox.Width = _width;
            _BoundingBox.Height = _height;
            

        }

        #region Intersection Routines
       
        /// <summary>
        /// Determines if there is overlap of the non-transparent pixels
        /// between two sprites.
        /// </summary>
        /// <param name="rectangleA">Bounding rectangle of the first sprite</param>
        /// <param name="dataA">Pixel data of the first sprite</param>
        /// <param name="rectangleB">Bouding rectangle of the second sprite</param>
        /// <param name="dataB">Pixel data of the second sprite</param>
        /// <returns>True if non-transparent pixels overlap; false otherwise</returns>
        public bool IntersectPixels(Rectangle rectangleB, Color[] dataB, out List<Vector2> Points)
        {
            Points = new List<Vector2>();
            // Find the bounds of the rectangle intersection
            int top = Math.Max(_BoundingBox.Top, rectangleB.Top); //lowest highpoint
            int bottom = Math.Min(_BoundingBox.Bottom, rectangleB.Bottom);//highest lowpoint
            int left = Math.Max(_BoundingBox.Left, rectangleB.Left);//smallest leftpoint
            int right = Math.Min(_BoundingBox.Right, rectangleB.Right);//largest rightpoint

            // Check every point within the intersection bounds
            for (int y = top; y < bottom; y++)
            {
                for (int x = left; x < right; x++)
                {
                    // Get the color of both pixels at this point
                    Color colorA = _TextureData[(x - _BoundingBox.Left) +
                                         (y - _BoundingBox.Top) * _BoundingBox.Width];

                    Color colorB = dataB[(x - rectangleB.Left) +
                                         (y - rectangleB.Top) * rectangleB.Width];

                    // If both pixels are not completely transparent,
                    if (colorA.A != 0 && colorB.A != 0)
                    {
                        Points.Add(new Vector2(x, y));
                    }
                }
            }
            if (Points.Count > 0)
            {
                return true;
            }
            // No intersection found
            return false;
        }

        public bool IntersectPixels(GameObject lhs, out List<Vector2> Points)
        {
            Collidable temp = (lhs[ComponentType.Collidable] as Collidable);
            return IntersectPixels(temp._BoundingBox, temp._TextureData,out Points);
            //return IntersectPixels(_Transform, _width, _height, _TextureData, temp._Transform, temp._width, temp._height, temp._TextureData, out Points);
        }

        /// <summary>
        /// Determines if there is overlap of the non-transparent pixels between two
        /// sprites.
        /// </summary>
        /// <param name="transformA">World transform of the first sprite.</param>
        /// <param name="widthA">Width of the first sprite's texture.</param>
        /// <param name="heightA">Height of the first sprite's texture.</param>
        /// <param name="dataA">Pixel color data of the first sprite.</param>
        /// <param name="transformB">World transform of the second sprite.</param>
        /// <param name="widthB">Width of the second sprite's texture.</param>
        /// <param name="heightB">Height of the second sprite's texture.</param>
        /// <param name="dataB">Pixel color data of the second sprite.</param>
        /// <returns>True if non-transparent pixels overlap; false otherwise</returns>
        public bool IntersectPixels(
                            Matrix transformA, int widthA, int heightA, Color[] dataA,
                            Matrix transformB, int widthB, int heightB, Color[] dataB, out List<Vector2> Points)
        {
            Points = new List<Vector2>();
            // Calculate a matrix which transforms from A's local space into
            // world space and then into B's local space
            Matrix transformAToB = transformA * Matrix.Invert(transformB);

            // When a point moves in A's local space, it moves in B's local space with a
            // fixed direction and distance proportional to the movement in A.
            // This algorithm steps through A one pixel at a time along A's X and Y axes
            // Calculate the analogous steps in B:
            Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB);
            Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB);

            // Calculate the top left corner of A in B's local space
            // This variable will be reused to keep track of the start of each row
            Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB);

            // For each row of pixels in A
            for (int yA = 0; yA < heightA; yA++)
            {
                // Start at the beginning of the row
                Vector2 posInB = yPosInB;

                // For each pixel in this row
                for (int xA = 0; xA < widthA; xA++)
                {
                    // Round to the nearest pixel
                    int xB = (int)Math.Round(posInB.X);
                    int yB = (int)Math.Round(posInB.Y);

                    // If the pixel lies within the bounds of B
                    if (0 <= xB && xB < widthB &&
                        0 <= yB && yB < heightB)
                    {
                        // Get the colors of the overlapping pixels
                        Color colorA = dataA[xA + yA * widthA];
                        Color colorB = dataB[xB + yB * widthB];

                        // If both pixels are not completely transparent,
                        if (colorA.A != 0 && colorB.A != 0)
                        {
                            // then an intersection has been found
                            Points.Add(new Vector2(xA,yA));
                        }
                    }

                    // Move to the next pixel in the row
                    posInB += stepX;
                }

                // Move to the next row
                yPosInB += stepY;
            }

            if (Points.Count > 0)
            {
                return true;
            }
            else
            {
                // No intersection found
                return false;
            }
        }

        public bool IntersectPixels(Collidable p_CollidableComponent, out List<Vector2> Points)
        {
            return IntersectPixels(p_CollidableComponent.GetBoundingBox, p_CollidableComponent._TextureData,out Points);
        }

        /// <summary>
        /// Calculates an axis aligned rectangle which fully contains an arbitrarily
        /// transformed axis aligned rectangle.
        /// </summary>
        /// <param name="rectangle">Original bounding rectangle.</param>
        /// <param name="transform">World transform of the rectangle.</param>
        /// <returns>A new rectangle which contains the trasnformed rectangle.</returns>
        public Rectangle CalculateBoundingRectangle(Rectangle rectangle,
                                                           Matrix transform)
        {
            // Get all four corners in local space
            Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top);
            Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top);
            Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
            Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);

            // Transform all four corners into work space
            Vector2.Transform(ref leftTop, ref transform, out leftTop);
            Vector2.Transform(ref rightTop, ref transform, out rightTop);
            Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
            Vector2.Transform(ref rightBottom, ref transform, out rightBottom);

            // Find the minimum and maximum extents of the rectangle in world space
            Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
                                      Vector2.Min(leftBottom, rightBottom));
            Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
                                      Vector2.Max(leftBottom, rightBottom));

            // Return that as a rectangle
            return new Rectangle((int)min.X, (int)min.Y,
                                 (int)(max.X - min.X), (int)(max.Y - min.Y));
        }

        #endregion
    }

}
